package org.example.domain.strategy.service.armory;

import org.example.domain.strategy.model.entity.StrategyAwardEntity;
import org.example.domain.strategy.model.entity.StrategyEntity;
import org.example.domain.strategy.model.entity.StrategyRuleEntity;
import org.example.domain.strategy.repository.IStrategyRepository;
import org.example.types.common.Constants;
import org.example.types.enums.ResponseCode;
import org.example.types.exception.AppException;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.security.SecureRandom;
import java.util.*;

@Service
public class StrategyArmory implements IStrategyArmory,IStrategyDispatch{
    @Resource
    private IStrategyRepository repository;


    @Override
    public boolean assembleLotteryStrategy(Long strategyId) {
        //1.查询策略配置
        List<StrategyAwardEntity> strategyAwardEntities = repository.queryStrategyAwardEntitylist(strategyId);

        //2.库存数据装配
        for(StrategyAwardEntity strategyAwardEntity : strategyAwardEntities) {
            Integer awardId = strategyAwardEntity.getAwardId();
            Integer awardCount = strategyAwardEntity.getAwardCount();
            cacheAwardCount(strategyId,awardId,awardCount);
        }
        //3.全量数据装配
        assembleLotteryStrategy( String.valueOf(strategyId),strategyAwardEntities);
        //4.权重策略配置 -- 权重规则配置
        StrategyEntity strategyEntity = repository.queryStrategyEntityBystrategyId(strategyId);

        String ruleWeight = strategyEntity.getRuleWeight();
        if (null == ruleWeight) return true;
        //5.策略配置组装
        //5.1 获取策略规则模型
        StrategyRuleEntity strategyRuleEntity =  repository.queryStrategyRule(strategyId,ruleWeight);
        if (null == strategyRuleEntity){
        throw new AppException(ResponseCode.STRATEGY_RULE_WEIGHT_IS_NULL.getCode(), ResponseCode.STRATEGY_RULE_WEIGHT_IS_NULL.getInfo());
    }
        Map<String, List<Integer>> ruleWeightValueMap = strategyRuleEntity.getRuleValues();
        Set<String> keys = ruleWeightValueMap.keySet();
        for (String key : keys){
            List<Integer> ruleValue = ruleWeightValueMap.get(key);
            ArrayList<StrategyAwardEntity> strategyAwardEntitiesClone = new ArrayList<>(strategyAwardEntities);
            strategyAwardEntitiesClone.removeIf(entity -> !ruleValue.contains(entity.getAwardId()));
            assembleLotteryStrategy(String.valueOf(strategyId).concat("_").concat(key), strategyAwardEntitiesClone);
        }
        return true;
    }

    @Override
    public boolean assembleLotteryStrategyByActivityId(Long activityId) {
        Long strategyId = repository.queryStrategyIdByactivityId(activityId);
        return assembleLotteryStrategy(strategyId);
    }

    private void cacheAwardCount(Long strategyId,Integer awardId, Integer awardCount) {
        if (awardCount == null ||awardCount < 0){
            throw new RuntimeException("库存在数据库中未配置或者库存不足");
        }
        String key = Constants.RedisKey.STRATEGY_AWARD_COUNT_KEY +strategyId + Constants.UNDERLINE + awardId;
        repository.storeStrategyAwardCountByawardId(key,awardCount);
    }

    private void assembleLotteryStrategy(String key,List<StrategyAwardEntity> strategyAwardEntities){
        //1.获取最小概率值
        BigDecimal minAwardRate = strategyAwardEntities.stream()
                .map(StrategyAwardEntity::getAwardRate)
                .min(BigDecimal::compareTo)
                .orElse(BigDecimal.ZERO);
        //2.获取概率值总和
        BigDecimal totalAwardRate = strategyAwardEntities.stream()
                .map(StrategyAwardEntity::getAwardRate)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        //3.算出概率范围
        BigDecimal RateRange = totalAwardRate.divide(minAwardRate, 0, RoundingMode.CEILING);
        //4.
        ArrayList<Integer> strategyAwardSearchTables = new ArrayList<>(RateRange.intValue());
        for (StrategyAwardEntity strategyAwardEntity:strategyAwardEntities){
            BigDecimal awardRate = strategyAwardEntity.getAwardRate();
            Integer awardId = strategyAwardEntity.getAwardId();
            for (int i=0;i<RateRange.multiply(awardRate).setScale(0,RoundingMode.CEILING).intValue();i++){
                strategyAwardSearchTables.add(awardId);
            }
        }
        //5.乱序
        Collections.shuffle(strategyAwardSearchTables);

        HashMap<Integer,Integer> shufflestrategyAwardSearchTables = new HashMap<>();
        //6.
        for (int i =0; i<strategyAwardSearchTables.size();i++){
            shufflestrategyAwardSearchTables.put(i,strategyAwardSearchTables.get(i));
        }
        //7.存储到redis
        repository.storeStrategyAwardSerrchTables(key,shufflestrategyAwardSearchTables.size(),shufflestrategyAwardSearchTables);
    }
    @Override
    public Integer getRandomAwardId(Long strategyId) {
        int rateRange =  repository.getRateRange(strategyId);
        return repository.getStrategyAwardAssemble(String.valueOf(strategyId),new SecureRandom().nextInt(rateRange));
    }

    @Override
    public Integer getRandomAwardId(Long strategyId, String ruleWeirhtValue) {

        String key = String.valueOf(strategyId).concat("_").concat(ruleWeirhtValue);

        int rateRange = repository.getRateRange(key);

        return repository.getStrategyAwardAssemble(key,new SecureRandom().nextInt(rateRange));
    }

    @Override
    public Boolean subtractionAwardStock(Long strategyId, Integer awardId) {
        String CasheKey = Constants.RedisKey.STRATEGY_AWARD_COUNT_KEY + strategyId + "_" +awardId;
        return  repository.subtractionAwardStock(CasheKey);

    }
}
